package templateExecel2.extract;

import templateExecel2.utils.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class ValueHandler {

    private static Map<String, Class<?>> entityMapping;

    public static void setEntityMapping(Map<String, Class<?>> entityMapping) {
        ValueHandler.entityMapping = entityMapping;
    }

    //需要将数据处理为一个map
    static Map<String, Object> handle(Map<String, List<ValueHolder>> map) {

        Map<String, Object> r = new HashMap<>();

        H h = new H() {
            @Override
            void transmit() {//

                //对每个元素而言总是对齐的

                for (H value : subs.values()) {

                    //计算所有的和
                    int sum = 0;
                    for (ValueHolder holder : value.valueHolders) {
                        sum += holder.getRow();
                    }
                    value.forward(r, sum);
                }

            }
        };

        List<H> list = new ArrayList<>();

        map.forEach((k, v) -> {

            H l = h;

            String[] split = k.split("\\.");

            String n = "";

            //找到最后一个节点
            for (String s : split) {
                l = l.getSub(s);
                n += s;
                l.fullExp = n;
                n = n + ".";
            }



            l.valueHolders = v;

            Class<?> aClass = entityMapping.get(l.fullExp);
            if (aClass != null) {
                if (aClass == Integer.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(Double.valueOf(d.getValue().toString()).intValue());
                        }
                    });
                } else if (aClass == Character.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(Character.highSurrogate(Double.valueOf(d.getValue().toString()).intValue()));
                        }
                    });
                } else if (aClass == Long.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(Double.valueOf(d.getValue().toString()).longValue());
                        }
                    });
                } else if (aClass == Double.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(Double.valueOf(d.getValue().toString()));
                        }
                    });
                } else if (aClass == String.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(String.valueOf(d.getValue().toString()));
                        }
                    });
                } else if (aClass == Boolean.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(Boolean.valueOf(d.getValue().toString()));
                        }
                    });
                } else if (aClass == Short.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(Double.valueOf(d.getValue().toString()).shortValue());
                        }
                    });
                } else if (aClass == Byte.class) {
                    v.forEach(d -> {
                        if (d.getValue() != null) {
                            d.setValue(Double.valueOf(d.getValue().toString()).byteValue());
                        }
                    });
                }
            }


            list.add(l);

        });

        for (H h1 : list) {
            h1.transmit();
        }

        return r;
    }


    private static class H {

        private String name;


        private String setterMethodName;

        void setName(String name) {
            this.name = name;
            this.setterMethodName = "set" + name.substring(0, 1).toUpperCase();
            if (name.length() > 1) {
                this.setterMethodName += name.substring(1);
            }
        }

        private String exp;

        private String fullExp;

        //变化的自变量,如果存在表明是一个数组元素
        private String independent;

        //父级节点
        private H parent;

        //模板中变量数通常不多
        Map<String, H> subs;


        private List<ValueHolder> valueHolders;

        H getSub(String exp) {
            if (subs == null) {
                subs = new HashMap<>();
            }

            return subs.computeIfAbsent(exp, k -> {
                H h = new H();
                StringUtils.SubStringResult string = StringUtils.pairingSubString(exp, 0, '[', ']');
                if (string == null) {
                    h.setName(exp);
                    h.exp = exp;
                } else {
                    String name = exp.substring(0, string.getStart());
                    String ind = exp.substring(string.getStart() + 1, string.getEnd() - 1);
                    h.setName(name);
                    h.exp = exp;
                    h.independent = ind;
                }
                h.parent = H.this;
                return h;
            });
        }

        //下一轮的开始索引位置
        private int index = 0;

        private List<Object> findValue(int span) {
            int i;
            List<Object> r = new ArrayList<>();
            int sum = 0;
            for (i = index; i < valueHolders.size(); i++) {
                ValueHolder holder = valueHolders.get(i);
                r.add(holder.getValue());
                sum += holder.getRow();
                if (sum == span) {
                    i++;
                    break;
                } else if (sum > span) {
                    //TODO sum只能小于span或者等于
                    throw new RuntimeException("数组元素无法对齐");
                }
            }
            index = i;
            return r;
        }

        private Set<String> subExps;

        //接受后继节点反向调用
        void back(H sub) {

            if (subExps == null) {
                subExps = new HashSet<>(subs.keySet());
            }
            subExps.remove(sub.exp);
            //如果处理完成
            if (subExps.size() == 0) {
                transmit();
            }

        }

        //整合所有直接子节点,创建自己的节点
        private void handleValueHolder() {
            List<List<ValueHolder>> hs = new ArrayList<>();
            subs.values().forEach(v -> hs.add(v.valueHolders));

            //加和
            Map<List<ValueHolder>, Integer> sumMap = new HashMap<>();

            //索引
            Map<List<ValueHolder>, Integer> indexMap = new HashMap<>();

            for (List<ValueHolder> h : hs) {
                sumMap.put(h, 0);
                indexMap.put(h, 0);
            }


            while (true) {

                int maxSum = 0;

                //该元素占用的单元格长度
                int row = 0;

                boolean flag = false;

                //找到某行最大的
                for (List<ValueHolder> h : hs) {
                    Integer index = indexMap.get(h);
                    ValueHolder holder = h.get(index);

                    int ind = index + 1;

                    //指针后移
                    indexMap.put(h, ind);

                    Integer sum = sumMap.get(h);

                    int currentSum = sum + holder.getRow();

                    if (currentSum > maxSum) {
                        maxSum = currentSum;
                        row = holder.getRow();
                        if (ind == h.size()) {
                            flag = true;
                        }
                    }

                    sumMap.put(h, currentSum);

                }


                for (List<ValueHolder> h : hs) {
                    Integer sum = sumMap.get(h);
                    if (sum < maxSum) {

                        int i = indexMap.get(h);
                        for (; true; i++) {
                            if (i >= h.size()) {
                                throw new RuntimeException("无法对齐元素");
                            }
                            ValueHolder holder = h.get(i);
                            sum += holder.getRow();
                            if (sum == maxSum) {
                                i++;
                                sumMap.put(h, maxSum);
                                break;
                            }
                        }
                        indexMap.put(h, i);

                    }
                }


                if (valueHolders == null) {
                    valueHolders = new LinkedList<>();
                }


                //TODO 判断是否具有实体映射,如果有实体映射就放入创建一个实体映射,否则放入一个HashMap

                Class<?> aClass = entityMapping.get(fullExp);

                if (aClass == null) {
                    valueHolders.add(new ValueHolder(new HashMap<String, Object>(), row));
                } else {
                    try {
                        Object o = aClass.newInstance();
                        valueHolders.add(new ValueHolder(o, row));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }


                //判断是否已经处理到最后了

                if (flag) {
                    break;
                }

            }

        }

        //触发传递
        void transmit() {
            if (subs != null) {

                handleValueHolder();

                //告知自己的正向节点中数组元素,进行必要的合并
                for (ValueHolder holder : valueHolders) {
                    for (H value : subs.values()) {
                        value.forward(holder.getValue(), holder.getRow());
                    }
                }
            }

            if (parent != null) {
                parent.back(this);
            }

        }

        void forward(Object f, int row) {

            if (independent == null) {
                List<Object> list = findValue(row);
                if (list.size() != 1) {
                    throw new RuntimeException("元素长度无法对齐");
                }

                if (f instanceof Map) {
                    Map map = (Map) f;
                    map.put(name, list.get(0));

                } else {
                    try {
                        Object o = list.get(0);
                        Method method = f.getClass().getMethod(setterMethodName, o.getClass());
                        method.invoke(f, o);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }

            } else {
                List<Object> list = findValue(row);
                if (f instanceof Map) {
                    Map map = (Map) f;
                    map.put(name, list);

                } else {
                    try {
                        Method method = f.getClass().getMethod(setterMethodName, List.class);
                        method.invoke(f, list);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }

        }


    }


    public static void main(String[] args) {

        A.class.getMethods();
    }

    public static class A {

        public void test(List<Integer> i) {

        }

    }



}
